package com.salton123.whiteboard.widget

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.graphics.Shader
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import com.salton123.whiteboard.Constant
import com.salton123.whiteboard.callback.IBoardCallback
import com.salton123.whiteboard.model.bean.StrokeType
import com.salton123.whiteboard.model.bean.SvgPath
import com.salton123.whiteboard.model.bean.WhiteBoardData
import com.salton123.whiteboard.model.record.IRecord
import com.salton123.whiteboard.model.record.PathRecord
import com.salton123.whiteboard.utils.MatrixUtils.translateX
import com.salton123.whiteboard.utils.MatrixUtils.translateY
import com.salton123.whiteboard.widget.TouchGestureDetector.OnTouchGestureListener

/**
 * User: wujinsheng1@yy.com
 * Date: 2020/4/29 19:37
 * ModifyTime: 19:37
 * Description:
 */
class BoardView @JvmOverloads
constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : BaseBoardView(context, attrs, defStyleAttr),
        StrokePopup.OnStrokeCallback {
    private var mLastX = 0f
    private var mLastY = 0f
    var strokeColor: Int = Constant.DEFAULT_DEFAULT_STROKE_COLOR
    var strokeSize: Float = Constant.DEFAULT_STROKE_SIZE
    var strokeAlpha: Int = Constant.DEFAULT_STROKE_ALPHA
    private var mBoardCallback: IBoardCallback? = null

    fun setBoardAction(callback: IBoardCallback) {
        this.mBoardCallback = callback
    }

    private val mTouchGestureDetector by lazy(LazyThreadSafetyMode.PUBLICATION) {
        TouchGestureDetector(context, object : OnTouchGestureListener() {
            var mRectF = RectF()
            private var downX: Float = 0F
            private var downY: Float = 0F

            override fun onDown(e: MotionEvent): Boolean {
                downX = e.x
                downY = e.y
                return super.onDown(e)
            }

            override fun onSingleTapUp(e: MotionEvent): Boolean {
                val findItem = mCurrentData.strokeRecordList.find { record ->
                    record.path.computeBounds(mRectF, true) //计算画线轨迹的矩形范围
                    val matrix = record.matrix
                    val translateX = translateX(matrix)
                    val translateY = translateY(matrix)
                    mRectF.offset(translateX, translateY)
                    mRectF.contains(e.x, e.y)
                }
                mCurrentData.selectedRecord = findItem
                if (findItem != null) {
                    if (mCurrentData.isEraseMode()) {
                        mCurrentData.strokeRecordList.remove(mCurrentData.selectedRecord)
                        mCurrentData.selectedRecord = null
                    }
                }
                notifyRender()
                return true
            }

            override fun onScrollBegin(e: MotionEvent) {
                Log.d(TAG, "onScrollBegin: ")
                if (mCurrentData.isStrokeMode()) {
                    val strokePath = SvgPath()
                    val record: IRecord = PathRecord(strokePath, createPaint())
                    record.strokeAlpha = strokeAlpha
                    record.strokeColor = strokeColor
                    record.strokeSize = strokeSize
                    mCurrentData.curRecord = record // 新的涂鸦
                    mCurrentData.strokeRecordList.add(record)
                    record.path.moveTo(e.x, e.y)
                    mLastX = e.x
                    mLastY = e.y
                }
                notifyRender()
            }

            override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
                Log.d(TAG, "onScroll: " + e2.x + " " + e2.y)
                if (mCurrentData.isStrokeMode()) { // 没有选中的涂鸦
                    when (mCurrentData.strokeType) {
                        StrokeType.STROKE_TYPE_GRAFFITO -> {
                            mCurrentData.curRecord?.path?.quadTo(mLastX, mLastY, (e2.x + mLastX) / 2,
                                    (e2.y + mLastY) / 2) // 使用贝塞尔曲线 让涂鸦轨迹更圆滑
                        }
                        StrokeType.STROKE_TYPE_LINE -> {
                            mCurrentData.curRecord?.path?.apply {
                                reset()
                                moveTo(downX, downY)
                                lineTo(e2.x, e2.y)
                            }
                        }
                        StrokeType.STROKE_TYPE_CIRCLE -> {
                            val tmpRect = RectF(downX, downY, e2.x, e2.y)
                            mCurrentData.curRecord?.path?.reset()
                            mCurrentData.curRecord?.path?.addOval(tmpRect, Path.Direction.CW)
                        }
                        StrokeType.STROKE_TYPE_RECTANGLE -> {
                            val tmpRect = RectF(downX, downY, e2.x, e2.y)
                            mCurrentData.curRecord?.path?.reset()
                            mCurrentData.curRecord?.path?.addRect(tmpRect, Path.Direction.CW)
                        }
                        else -> {
                        }
                    }
                    mLastX = e2.x
                    mLastY = e2.y
                } else if (mCurrentData.isSelectedMode()) { // 移动选中的涂鸦
                    mCurrentData.selectedRecord?.matrix?.let {
                        val translateX = translateX(it)
                        val translateY = translateY(it)
                        it.setTranslate(translateX - distanceX, translateY - distanceY)
                        Log.d(TAG,
                                "[onScroll] selected record: old tx: " + translateX + ",ty:" + translateY
                                        + ",dx:" + distanceX + ",dy:" + distanceX)
                    }
                }
                notifyRender()
                return true
            }

            override fun onScrollEnd(e: MotionEvent) { // 滑动结束
                Log.d(TAG, "onScrollEnd: ")
                if (mCurrentData.selectedRecord == null
                        && mCurrentData.strokeType != StrokeType.STROKE_TYPE_CIRCLE
                        && mCurrentData.strokeType != StrokeType.STROKE_TYPE_RECTANGLE
                ) {
                    mCurrentData.curRecord?.path?.quadTo(
                            mLastX,
                            mLastY,
                            (e.x + mLastX) / 2,
                            (e.y + mLastY) / 2) // 使用贝塞尔曲线 让涂鸦轨迹更圆滑
                    mCurrentData.curRecord = null // 轨迹结束
                }
            }

            override fun onUpOrCancel(e: MotionEvent?) {
                super.onUpOrCancel(e)
                mCurrentData.strokeRedoList.clear() //手指起来表示用户已经重新绘制
                mBoardCallback?.onAction(IBoardCallback.ACTION_UP, mCurrentData)
            }
        })
    }

    private fun createPaint(): Paint {
        val realColor = Color.argb(strokeAlpha,
                Color.red(strokeColor),
                Color.green(strokeColor),
                Color.blue(strokeColor))
        Log.d(TAG, "[createPaint] realColor:$realColor")
        val strokePaint = Paint()
        strokePaint.isAntiAlias = true
        strokePaint.isDither = true
        strokePaint.style = Paint.Style.STROKE
        strokePaint.color = realColor
        strokePaint.strokeJoin = Paint.Join.ROUND
        strokePaint.strokeCap = Paint.Cap.ROUND
        strokePaint.strokeWidth = strokeSize
        return strokePaint
    }

    /**
     * 选中模式下的边框画笔
     */
    private fun createSelectedBorderPaint(): Paint {
        val paint = Paint()
        paint.isAntiAlias = true
        paint.isDither = true
        paint.style = Paint.Style.STROKE
        paint.color = Color.parseColor("#44888888")
        paint.strokeJoin = Paint.Join.ROUND
        paint.strokeCap = Paint.Cap.ROUND
        paint.strokeWidth = 5f
        return paint
    }

    fun updateData(whiteBoardData: WhiteBoardData) {
        mCurrentData = whiteBoardData
        notifyRender()
    }

    fun createThumbnail() {
        mCurrentData.previewThumbnail = getResultBitmap()
    }

    private fun getResultBitmap(): Bitmap {
        val newBM = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
        val canvas = Canvas(newBM)
        drawBackground(canvas)
        drawRecord(canvas)
        canvas.save()
        canvas.restore()
        return newBM
    }

    fun onBoardType(boardType: Int) {
        mCurrentData.boardType = boardType
    }

    override fun onStrokeType(strokeType: Int) {
        mCurrentData.strokeType = strokeType
    }

    override fun onStrokeColor(color: Int) {
        strokeColor = color
    }

    override fun onStrokeAlpha(alpha: Int) {
        strokeAlpha = alpha
    }

    override fun onStrokeSize(newSize: Float) {
        strokeSize = newSize
    }

    override fun drawRecord(canvas: Canvas) {
        try {
            for (record in mCurrentData.strokeRecordList) {
                if (mCurrentData.selectedRecord == record && mCurrentData.isSelectedMode()) {
                    val boundRet = RectF()
                    record.path.computeBounds(boundRet, true) //计算画线轨迹的矩形范围
                    val matrix = record.matrix
                    val translateX = translateX(matrix)
                    val translateY = translateY(matrix)
                    boundRet.offset(translateX, translateY)
                    boundRet.inset(-record.strokeSize, -record.strokeSize)
                    canvas.drawRect(boundRet, createSelectedBorderPaint())
                }
                canvas.matrix = record.matrix
                record.draw(canvas)
            }
        } catch (tx: Throwable) {
            tx.printStackTrace()
        }
    }

    /**
     * 绘制背景
     */
    override fun drawBackground(canvas: Canvas) {
        mCurrentData.backgroundBitmap?.let {
            val matrix = Matrix()
            val paint = Paint()
            val shader = BitmapShader(it, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
            matrix.postScale(1f, 1f)    //不对素材进行缩放
            shader.setLocalMatrix(matrix)
            paint.shader = shader
            canvas.drawPaint(paint)
        } ?: canvas.drawColor(mCurrentData.backgroundColor)
    }

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        return if (!mTouchGestureDetector.onTouchEvent(event)) { // 由手势识别器处理手势
            super.dispatchTouchEvent(event)
        } else true
    }

    fun undo() {
        try {
            val last = mCurrentData.strokeRecordList.last()
            mCurrentData.strokeRedoList.add(last)
            mCurrentData.strokeRecordList.remove(last)
            notifyRender()
        } catch (ignore: Throwable) {
        }
        mBoardCallback?.onAction(IBoardCallback.ACTION_UP, mCurrentData)
    }

    fun redo() {
        try {
            val last = mCurrentData.strokeRedoList.last()
            mCurrentData.strokeRecordList.add(last)
            mCurrentData.strokeRedoList.remove(last)
            notifyRender()
        } catch (ignore: Throwable) {
        }
        mBoardCallback?.onAction(IBoardCallback.ACTION_UP, mCurrentData)
    }

    fun clearAll() {
        mCurrentData.strokeRecordList.clear()
        mCurrentData.strokeRedoList.clear()
        notifyRender()
    }

    companion object {
        private const val TAG = "BoardView"
        var mCurrentData = WhiteBoardData()
    }
}